package network

import (
	"context"
	"errors"
	"net"

	"github.com/gorilla/websocket"
)

var (
	// 不支持的network类型
	ErrorUnsupportedNetworkType = errors.New("unsupported network type")

	// 网络连接已关闭
	ErrorConnectionClosed = errors.New("connection has been closed")

	// 网络连接事件监听处理器无效
	ErrorEventHandlerInvalid = errors.New("use nil EventHandler to start connection")

	// 网络连接定时任务启动失败
	ErrorCrontabLuanchFailed = errors.New("crontab failed to be launch")

	// 消息编码出来的二进制数据为空
	ErrorMessageBufferSizeTooShort = errors.New("message buffer too short")

	// UDP消息包未指定对端地址
	ErrorUDPPacketPeerAddrNotSpecify = errors.New("udp packet peer address not specify")

	// UDP消息包大小过大
	ErrorUDPPacketSizeTooLarge = errors.New("udp packet size too large")

	// TLS配置为空
	ErrorTLSConfigIsNil = errors.New("TLS config is nil")

	// 每次读Socket数据大小上限 32k
	DefaultReadLimit = 4 * 1024

	// 每次写Socket数据大小上限 32k
	DefaultWriteLimit = 16 * 1024

	// UDP包大小上限 大于该值Socket会报错
	MaxUDPPacketSize = 65507

	// 发送消息队列容量
	SendChanCap = 1000
)

// 监听器类型
type ListenerType uint8

const (
	Listener_UDP ListenerType = 0
	Listener_TCP ListenerType = 1
	Listener_WS  ListenerType = 2
	Listener_WSS ListenerType = 3
)

// 网络连接类型
type ConnectionType uint8

const (
	Connection_UDP ConnectionType = 0
	Connection_TCP ConnectionType = 1
	Connection_WS  ConnectionType = 2
	Connection_WSS ConnectionType = 3
)

// 监听器 网络连接 状态类型
type StatusType uint8

const (
	Status_Uninit  StatusType = 0 // 未初始化
	Status_Init    StatusType = 1 // 已初始化
	Status_Opening StatusType = 2 // 运行中
	Status_Closed  StatusType = 3 // 已关闭
)

// 监听指定网络端口
func Listen(ctx context.Context, options ...Option) (Listener, error) {
	opts := LoadOptions(options...)
	switch opts.Network {
	case "tcp", "tcp4", "tcp6":
		return NewTCPListener(ctx, opts.Network, opts.Address)
	case "udp", "udp4", "udp6":
		return NewUDPListener(ctx, opts.Network, opts.Address)
	case "ws":
		return NewWSListener(ctx, opts.Network, opts.Address, opts.WSPath)
	case "wss":
		return NewWSSListener(ctx, opts.Network, opts.Address, opts.WSPath, opts.WSTlsConfig)
	default:
		return nil, &networkError{ErrorType_Network, ErrorUnsupportedNetworkType}
	}
}

// 连接到指定网络端口
func Dial(ctx context.Context, options ...Option) (Conn, error) {
	opts := LoadOptions(options...)
	switch opts.Network {
	case "tcp", "tcp4", "tcp6":
		raddr, underlyingErr := net.ResolveTCPAddr(opts.Network, opts.Address)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}

		rawConn, underlyingErr := net.DialTCP(opts.Network, nil, raddr)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}

		return NewTCPConn(nil, rawConn), nil
	case "udp", "udp4", "udp6":
		raddr, underlyingErr := net.ResolveUDPAddr(opts.Network, opts.Address)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}

		rawConn, underlyingErr := net.DialUDP(opts.Network, nil, raddr)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}
		return NewUDPConn(nil, rawConn, false), nil
	case "ws":
		url := opts.Network + "://" + opts.Address + opts.WSPath
		rawConn, _, underlyingErr := websocket.DefaultDialer.Dial(url, nil)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}
		return NewWSConn(nil, rawConn, false), nil
	case "wss":
		url := opts.Network + "://" + opts.Address + opts.WSPath

		dialer := *(websocket.DefaultDialer)
		dialer.TLSClientConfig = opts.WSTlsConfig
		rawConn, _, underlyingErr := dialer.Dial(url, nil)
		if underlyingErr != nil {
			return nil, &networkError{ErrorType_Underlying, underlyingErr}
		}

		return NewWSConn(nil, rawConn, true), nil
	default:
		return nil, &networkError{ErrorType_Network, ErrorUnsupportedNetworkType}
	}
}
